package com.atguigu.tingshu.album.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.AuditService;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.AlbumStat;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.*;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.atguigu.tingshu.common.constant.SystemConstant.*;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

	@Autowired
	private TrackInfoMapper trackInfoMapper;

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	private VodService vodService;

	@Autowired
	private TrackStatMapper trackStatMapper;

	@Autowired
	private AuditService auditService;

	@Autowired
	private UserFeignClient userFeignClient;

	@Autowired
	private AlbumStatMapper albumStatMapper;


	/**
	 * 保存声音
	 *
	 * @param trackInfoVo
	 * @param userId
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId) {
		//1.根据所属专辑ID查询专辑信息 得到封面图片，用于后续更新
		Long albumId = trackInfoVo.getAlbumId();
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		Integer includeTrackCount = albumInfo.getIncludeTrackCount();
		if (albumInfo == null){
			log.error("专辑:{}不存在",albumId);
			throw new GuiguException(404,"专辑不存在");
		}
		//2.新增声音记录
		//2.1 将声音VO转为PO
		TrackInfo trackInfo = BeanUtil.copyProperties(trackInfoVo, TrackInfo.class);
		//2.2 给属性赋值
		//2.2.1 基础属性：用于ID，审核状态（审核不通过）
		trackInfo.setUserId(userId);
		trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);
		//2.2.2 设置声音序号 要求从1开始递增
		trackInfo.setOrderNum(albumInfo.getIncludeTrackCount() + 1);
		//2.2.3 封面图片 如果未提交使用所属专辑封面
		String coverUrl = trackInfo.getCoverUrl();
		if (StringUtils.isBlank(coverUrl)){
			trackInfo.setCoverUrl(albumInfo.getCoverUrl());
		}

		//.对点播平台音频文件进行审核（异步审核）
		String content = trackInfo.getTrackTitle() + trackInfo.getTrackIntro();
		String suggestion = auditService.auditText(content);
		if ("pass".equals(suggestion)) {
			trackInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
			//TODO 对音频进行审核，对音频文件发起审核任务，得到审核任务ID-状态：审核中
			String reviewTaskId = auditService.startReviewTask(trackInfo.getMediaFileId());
			trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
			trackInfo.setReviewTaskId(reviewTaskId);
		} else if ("review".equals(suggestion)) {
			trackInfo.setStatus(SystemConstant.ALBUM_STATUS_REVIEW);
		} else if ("block".equals(suggestion)) {
			trackInfo.setStatus(SystemConstant.ALBUM_STATUS_NO_PASS);
		}

		//2.2.4 调用点播平台获取音频详情信息：时长、大小、类型
		TrackMediaInfoVo trackMediaInfoVo = vodService.getTrackMediaInfo(trackInfo.getMediaFileId());
		if (trackMediaInfoVo != null){
			trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfoVo.getDuration()));
			trackInfo.setMediaSize(trackMediaInfoVo.getSize());
			trackInfo.setMediaType(trackMediaInfoVo.getType());
		}

		//2.3 新增声音记录/保存声音
		trackInfoMapper.insert(trackInfo);
		Long trackId = trackInfo.getId();

		//3.新增/保存声音统计记录
		this.savaTrackStat(trackId,SystemConstant.TRACK_STAT_PLAY,0);
		this.savaTrackStat(trackId,SystemConstant.TRACK_STAT_COLLECT,0);
		this.savaTrackStat(trackId,SystemConstant.TRACK_STAT_PRAISE,0);
		this.savaTrackStat(trackId,SystemConstant.TRACK_STAT_COMMENT,0);

		//4. 更新专辑信息：包含声音数量
		albumInfo.setIncludeTrackCount(includeTrackCount + 1);
		albumInfoMapper.updateById(albumInfo);


		//6.对点播平台音频文件进行审核（异步审核）
		String reviewTaskId = auditService.startReviewTask(trackInfo.getMediaFileId());
		trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
		trackInfo.setReviewTaskId(reviewTaskId);
		trackInfoMapper.updateById(trackInfo);
	}



	/**
	 * 保存声音统计信息
	 * @param trackId 声音ID
	 * @param statType 统计类型
	 * @param statNum 统计数值
	 */
	@Override
	public void savaTrackStat(Long trackId, String statType, int statNum) {
		TrackStat trackStat = new TrackStat();
		trackStat.setTrackId(trackId);
		trackStat.setStatType(statType);
		trackStat.setStatNum(statNum);
		trackStatMapper.insert(trackStat);
	}


	/**
	 * 条件分页查询当前用户声音列表（包含声音统计信息）
	 *
	 * @param pageInfo 分页对象
	 * @param trackInfoQuery 查询条件（用户ID，关键字，审核状态）
	 * @return
	 */
	@Override
	public Page<TrackListVo> getUserTrackPage(Page<TrackListVo> pageInfo, TrackInfoQuery trackInfoQuery) {
		return trackInfoMapper.getUserTrackPage(pageInfo,trackInfoQuery);
	}


	/**
	 * 修改/更新声音信息
	 *
	 * @param id
	 * @param trackInfo
	 * @return
	 */
	@Override
	public void updateTrackInfo(Long id,TrackInfoVo trackInfoVo) {
		/*//1.判断音频文件是否变更
		//1.1 根据声音ID查询声音记录得到“旧”的音频文件标识
		TrackInfo oldTrackInfo = trackInfoMapper.selectById(trackInfo.getId());
		//1.2 判断文件是否被更新
		if (!trackInfo.getMediaFileId().equals(oldTrackInfo.getMediaFileId())){
			//1.3 如果文件被更新，再次获取新音频文件信息更新：时长，大小，类型
			TrackMediaInfoVo mediaInfoVo = vodService.getTrackMediaInfo(trackInfo.getMediaFileId());
			if (mediaInfoVo != null){
				trackInfo.setMediaType(mediaInfoVo.getType());

				trackInfo.setMediaDuration(BigDecimal.valueOf(mediaInfoVo.getDuration()));
				trackInfo.setMediaSize(mediaInfoVo.getSize());
				trackInfo.setStatus(SystemConstant.TRACK_STATUS_NO_PASS);

				// 音频文件发生更新后，必须再次进行审核
				//4. 开启音视频任务审核；更新声音表：审核任务ID-后续采用定时任务检查审核结果
				//4.1 启动审核任务得到任务ID
				//String reviewTaskId = vodService.reviewMediaTask(trackInfo.getMediaFileId());
				//4.2 更新声音表：审核任务ID，状态（审核中）

				//4.2 更新声音表：审核任务ID，状态（审核中）
				//trackInfo.setReviewTaskId(reviewTaskId);
				//trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEW_ING);
				//trackInfoMapper.updateById(trackInfo);
			}
			//1.4 从点播平台删除旧的音频文件
			vodService.deleteMedia(oldTrackInfo.getMediaFileId());
		}
		//2.更新声音信息
		trackInfoMapper.updateById(trackInfo);*/



		//1.判断音频是否修改过
		//1.1 根据声音ID查询声音记录，得到更新前音频ID
		TrackInfo trackInfo = trackInfoMapper.selectById(id);
		String oldMediaFileId = trackInfo.getMediaFileId();

		//将更新其他信息拷贝到声音对象中，注意：内部音频相关url、文件唯一标识、时间、大小、类型
		BeanUtil.copyProperties(trackInfoVo, trackInfo);

		//1.2 对修改后的内容进行审核,文本审核通过，对音频文件内容进行审核
		String content = trackInfoVo.getTrackTitle() + trackInfoVo.getTrackIntro();
		String suggestion = auditService.auditText(content);
		if ("pass".equals(suggestion)) {
			trackInfo.setStatus(SystemConstant.ALBUM_STATUS_PASS);
		} else if ("review".equals(suggestion)) {
			trackInfo.setStatus(SystemConstant.ALBUM_STATUS_REVIEW);
		} else if ("block".equals(suggestion)) {
			trackInfo.setStatus(SystemConstant.ALBUM_STATUS_NO_PASS);
		}

		//2.判断音频文件是否改变
		// 跟参数中提交音频ID进行比较
		String newMediaFileId = trackInfoVo.getMediaFileId();
		if (!oldMediaFileId.equals(newMediaFileId)) {
			//2.1从点播平台获取新音频文件信息得到时长、大小、类型 更新声音对象
			TrackMediaInfoVo trackMediaInfo = vodService.getTrackMediaInfo(newMediaFileId);
			trackInfo.setMediaType(trackMediaInfo.getType());
			trackInfo.setMediaDuration(BigDecimal.valueOf(trackMediaInfo.getDuration()));
			trackInfo.setMediaSize(trackMediaInfo.getSize());

/*			trackInfo.setMediaUrl(trackInfoVo.getMediaUrl());
			trackInfo.setMediaFileId(newMediaFileId);*/

			//TODO 对音频进行审核，对音频文件发起审核任务，得到审核任务ID-状态：审核中
			String reviewTaskId = auditService.startReviewTask(trackInfo.getMediaFileId());
			trackInfo.setStatus(SystemConstant.TRACK_STATUS_REVIEWING);
			trackInfo.setReviewTaskId(reviewTaskId);

			//3.将原来旧文件从点播平台删除
			vodService.deleteMedia(oldMediaFileId);
			//TODO 对新音频文件内容进行审核
		}
		//4.更新声音记录，审核状态：未通过
		trackInfoMapper.updateById(trackInfo);
	}


	/**
	 * 删除声音记录
	 *
	 * @param id
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void removeTrackInfo(Long id) {
		//1.根据ID查询欲被删除声音记录-得到专辑ID跟声音序号
		TrackInfo deleteTrackInfo = trackInfoMapper.selectById(id);
		Long albumId = deleteTrackInfo.getAlbumId();
		Integer orderNum = deleteTrackInfo.getOrderNum();

		//2.更新声音表序号-确保连续
		LambdaUpdateWrapper<TrackInfo> updateWrapper = new LambdaUpdateWrapper<>();
		//2.1 更新条件
		updateWrapper.eq(TrackInfo::getAlbumId,albumId);
		updateWrapper.gt(TrackInfo::getOrderNum,orderNum);
		//2.2 修改字段值
		updateWrapper.setSql("order_num = order_num - 1");
		trackInfoMapper.update(null ,updateWrapper);

		//3.删除声音表记录
		trackInfoMapper.deleteById(id);

		//4.删除声音统计
		LambdaQueryWrapper<TrackStat> trackStatLambdaQueryWrapper = new LambdaQueryWrapper<>();
		trackStatLambdaQueryWrapper.eq(TrackStat::getTrackId,id);
		trackStatMapper.delete(trackStatLambdaQueryWrapper);

		//5.更新专辑包含声音数量
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
		albumInfoMapper.updateById(albumInfo);

		//6.删除点播平台音频文件
		vodService.deleteMedia(deleteTrackInfo.getMediaFileId());
	}


	/**
	 * 需求：用户未登录，可以给用户展示声音列表；用户已登录，可以给用户展示声音列表，并动态渲染付费标识
	 * 分页查询专辑下声音列表（动态渲染付费标识）
	 *
	 * @param pageInfo MP分页对象
	 * @param albumId  专辑ID
	 * @param userId   用户ID
	 * @return
	 */
	@Override
	public Page<AlbumTrackListVo> findAlbumTrackPage(Page<AlbumTrackListVo> pageInfo, Long albumId, Long userId) {
		//1.分页获取声音列表（包含统计数值） isShowPaidMark默认为false
		pageInfo = trackInfoMapper.findAlbumTrackPage(pageInfo, albumId);

		//动态渲染付费标识
		//2.根据专辑ID查询专辑信息，得到专辑付费类型以及免费试听的集数
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		Assert.notNull(albumInfo, "专辑{}不存在", albumId);

		//2.1 付费类型：0101-免费、0102-vip免费、0103-付费
		String payType = albumInfo.getPayType();
		//2.2 免费实体集数
		Integer tracksForFree = albumInfo.getTracksForFree();
		//3.如果用户未登录 情况一：且专辑付费类型是：VIP免费或付费，将除试听以外其他声音付费标识设置true
		if (userId == null) {
			if (ALBUM_PAY_TYPE_VIPFREE.equals(payType) || ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
				pageInfo.getRecords().stream().filter(t -> t.getOrderNum() > tracksForFree)
						.forEach(t -> t.setIsShowPaidMark(true));
			}
		} else {
		//4. 如果用户已登录 满足以下两种情况需要进一步获取当前页声音购买状态
		//4.1 远程调用用户服务获取用户身份，判断是否为会员（有效期内会员）
			UserInfoVo userInfoVo = userFeignClient.getUserInfoVo(userId).getData();
			Assert.notNull(userInfoVo,"用户{}不存在",userId);
			Boolean isVIP = false;
			if (userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().after(new Date())) {
				isVIP = true;
			}

			//4.2 情况二：如果专辑 付费类型：VIP免费 且 当前用户是普通用户，满足进一步查询当前页声音购买状态
			Boolean isNeedCheckPayStatus = false;
			if (!isVIP && ALBUM_PAY_TYPE_VIPFREE.equals(payType)) {
				isNeedCheckPayStatus = true;
			}

		//4.3 情况三：如果专辑 付费类型：付费，无论什么用户，满足进一步查询当前页声音购买状态
			if (ALBUM_PAY_TYPE_REQUIRE.equals(payType)) {
				isNeedCheckPayStatus = true;
			}

		//4.4 如果满足情况二或情况三，远程调用用户服务，得到当前页除试听部分每个声音购买状态
			if (isNeedCheckPayStatus) {
				//4.4.1 获取当前页中除试听以外其他声音ID列表
				List<Long> needCheckPayStatusTrackIdList = pageInfo
						.getRecords()
						.stream()
						.filter(t -> t.getOrderNum() > tracksForFree)
						.map(AlbumTrackListVo::getTrackId)
						.collect(Collectors.toList());

				//4.4.2 远程调用用户服务，获取当前页中声音购买状态Map<Long-专辑ID,Integer-购买状态>
				Map<Long, Integer> payStatusMap =
						userFeignClient.userIsPaidTrack(userId, albumId, needCheckPayStatusTrackIdList).getData();

				//4.5 根据响应声音购买状态，动态修改付费标识。购买状态为0的声音付费标识isShowPaidMark全部设置：true
				pageInfo.getRecords()
						.stream()
						.filter(t -> t.getOrderNum() > tracksForFree)
						.forEach(t -> t.setIsShowPaidMark(payStatusMap.get(t.getTrackId()) == 0));
			}
	}
		return pageInfo;
	}


	/**
	 * 更新DB中声音/专辑统计数值
	 *
	 * @param mqVo
	 */
	@Override
	public void updateTrackStat(TrackStatMqVo mqVo) {
		//1.更新声音统计数值
		//2.如果是"播放"or"评论"还需要更新专辑统计数值
		if (TRACK_STAT_PLAY.equals(mqVo.getStatType())) {
			//更新专辑统计表播放量
			albumStatMapper.update(
					null,
					new LambdaUpdateWrapper<AlbumStat>()
							.eq(AlbumStat::getAlbumId, mqVo.getAlbumId())
							.eq(AlbumStat::getStatType, ALBUM_STAT_PLAY)
							.setSql("stat_num = stat_num + " + mqVo.getCount())
			);
		}
		if (TRACK_STAT_COMMENT.equals(mqVo.getStatType())) {
			//更新专辑统计表评论量
			albumStatMapper.update(
					null,
					new LambdaUpdateWrapper<AlbumStat>()
							.eq(AlbumStat::getAlbumId,mqVo.getAlbumId())
							.eq(AlbumStat::getStatType,ALBUM_STAT_PLAY)
							.setSql("stat_num = stat_num + " + mqVo.getCount())
					);
		}
	}


	/**
	 * 查询声音统计信息
	 * @param trackId
	 * @return
	 */
	@Override
	public TrackStatVo getTrackStatVo(Long trackId) {
		return trackInfoMapper.getTrackStatVo(trackId);
	}


	/**
	 * 查询用户未购买声音列表
	 *
	 * @param trackId    选择购买声音ID，作为起始标准计算未购买声音数量
	 * @param trackCount 限制查询未购买声音数量
	 * 思路：
	 * 1. 根据声音Id 获取到当前声音对象
	 * 2. 远程调用用户服务获取已购买声音ID列表
	 * 3. 根据声音ID+当前声音序号查询待购声音列表，将已购声音进行排除
	 * 4. 返回待购声音集合对象返回
	 *
	 * @return
	 */
	@Override
	public List<TrackInfo> findWaitBuyTrackInfoList(Long userId, Long trackId, Integer trackCount) {

		//1.根据声音ID查询到“起始”待购声音记录-得到专辑ID及序号
		TrackInfo trackInfo = trackInfoMapper.selectById(trackId);

		Long albumId = trackInfo.getAlbumId();
		Integer orderNum = trackInfo.getOrderNum();

		//2.远程调用用户服务获取已购买声音ID列表
		List<Long> userPaidTrackList =
				userFeignClient.findUserPaidTrackList(albumId).getData();


		//3.构建查询条件查询未购买声音列表
		LambdaQueryWrapper<TrackInfo> queryWrapper = new LambdaQueryWrapper<TrackInfo>()
				.eq(TrackInfo::getAlbumId, albumId)
				.ge(TrackInfo::getOrderNum, orderNum)
				.orderByAsc(TrackInfo::getOrderNum)
				.select(TrackInfo::getId, TrackInfo::getTrackTitle, TrackInfo::getCoverUrl, TrackInfo::getAlbumId)
				.last(" limit " + trackCount);
		if (CollUtil.isNotEmpty(userPaidTrackList)) {
			queryWrapper.notIn(TrackInfo::getId,userPaidTrackList);
		}
		List<TrackInfo> trackInfoList = trackInfoMapper.selectList(queryWrapper);
		return trackInfoList;
	}
}
